package com.hm.base.auto.helper;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.hm.base.def.ScheduleJobStatusEnum;
import com.hm.base.domain.ScheduleJob;
import com.hm.base.mapper.ScheduleJobMapper;
import com.hm.common.def.DataStatusDef;
import com.hm.common.exception.ErrorCode;
import com.hm.common.exception.ServiceException;
import com.hm.common.util.CommonUtil;
import com.hm.common.util.StringUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * @author shishun.wang
 * @date 上午11:36:56 2017年8月24日
 * @version 1.0
 * @describe
 */
@Slf4j
@Component
@SuppressWarnings("unchecked")
public class ScheduleTriggerHandler {

	@Autowired
	private Scheduler scheduler;

	@Autowired
	private ScheduleJobMapper scheduleJobMapper;

	/**
	 * 添加定时任务
	 * 
	 * @param jobName
	 * @param jobGroup
	 * @param cron
	 */
	@Async
	public void addScheduleJob(String jobName, String jobGroup, String cron) {
		try {
			CronTrigger trigger = (CronTrigger) scheduler.getTrigger(TriggerKey.triggerKey(jobName, jobGroup));

			if (null != trigger) {
				log.warn("{}||{}||{}添加定时任务失败,CronTrigger已存在", jobName, jobGroup, cron);
				return;
			}
			// 按新的cronExpression表达式构建一个新的trigger
			trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup)
					.withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();

			// 把trigger和jobDetail注入到调度器
			scheduler.scheduleJob(JobBuilder.newJob((Class<? extends Job>) Class.forName(jobName))
					.withIdentity(jobName, jobGroup).build(), trigger);

		} catch (Exception e) {
			log.error("添加定时任务失败", e);
		}
	}

	/**
	 * 停止定时任务
	 * 
	 * @param jobName
	 * @param jobGroup
	 */
	@Async
	public void pauseScheduleJob(String jobName, String jobGroup) {
		try {
			JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
			scheduler.pauseJob(jobKey);// 停止触发器
		} catch (Exception e) {
			log.error("停止定时任务失败", e);
		}
	}

	/**
	 * 删除定时任务
	 * 
	 * @param jobName
	 * @param jobGroup
	 */
	@Async
	public void deleteScheduleJob(String jobName, String jobGroup) {
		try {
			TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
			JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
			scheduler.pauseJob(jobKey);// 停止触发器
			scheduler.unscheduleJob(triggerKey);// 移除触发器
			scheduler.deleteJob(jobKey);// 删除任务
		} catch (Exception e) {
			log.error("删除定时任务失败", e);
		}
	}

	/**
	 * 修改定时任务
	 * 
	 * @param jobName
	 * @param jobGroup
	 * @param cron
	 */
	@Async
	public void updateScheduleJob(String jobName, String jobGroup, String cron) {
		try {
			TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
			CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
			if (null == trigger) {
				this.addScheduleJob(jobName, jobGroup, cron);
				return;
			}

			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
			// 按新的cronExpression表达式重新构建trigger
			trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
			// 按新的trigger重新设置job执行
			scheduler.rescheduleJob(triggerKey, trigger);
		} catch (Exception e) {
			log.error("修改定时任务失败", e);
		}
	}

	/**
	 * 刷新定时任务
	 * 
	 * @param scheduleJobId
	 */
	@Async
	public void refreshScheduleJob(Long scheduleJobId) {
		if (CommonUtil.isEmpty(scheduleJobId)) {
			throw ServiceException.warning(ErrorCode.REQUIRED_PARAMETERS_MISSING);
		}

		ScheduleJob scheduleJob = scheduleJobMapper.selectByPrimaryKey(scheduleJobId);
		if (CommonUtil.isEmpty(scheduleJob)) {
			throw ServiceException.warning(ErrorCode.DATA_NOT_FOUND);
		}

		if (CommonUtil.isAnyEmpty(scheduleJob.getJobName(), scheduleJob.getJobGroup(), scheduleJob.getCron())) {
			return;
		}
		try {
			ScheduleJobStatusEnum jobStatus = ScheduleJobStatusEnum.trance(scheduleJob.getJobStatus());
			TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
			CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
			if (null == trigger && jobStatus == ScheduleJobStatusEnum.ENABLE) {
				// 创建JobDetail（数据库中job_name存的任务全路径，这里就可以动态的把任务注入到JobDetail中）
				JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(scheduleJob.getJobName()))
						.withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup()).build();
				// 表达式调度构建器
				CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCron());
				// 按新的cronExpression表达式构建一个新的trigger
				trigger = TriggerBuilder.newTrigger().withIdentity(scheduleJob.getJobName(), scheduleJob.getJobGroup())
						.withSchedule(scheduleBuilder).build();
				// 把trigger和jobDetail注入到调度器
				scheduler.scheduleJob(jobDetail, trigger);

				return;
			}

			if (jobStatus == ScheduleJobStatusEnum.DISABLE) {// 如果是禁用，从quartz中删除这条任务
				JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());

				scheduler.pauseJob(jobKey);// 停止触发器
				scheduler.unscheduleJob(triggerKey);// 移除触发器
				scheduler.deleteJob(jobKey);// 删除任务
				return;
			}

			if (!trigger.getCronExpression().equals(scheduleJob.getCron())) {
				// 说明该任务有变化，需要更新quartz中的对应的记录
				// 表达式调度构建器
				CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCron());
				// 按新的cronExpression表达式重新构建trigger
				trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
				// 按新的trigger重新设置job执行
				scheduler.rescheduleJob(triggerKey, trigger);
			}
		} catch (Exception e) {
			log.error(scheduleJobId + "刷新定时任务,失败", e);
		}
	}

	@Scheduled(cron = "${com.hm.base.init.refreshScheduleJobsTask}")
	public void refreshScheduleJobsTask() {
		this.refreshScheduleJobs();
	}

	/**
	 * 刷新所有定时任务
	 */
	@Async
	public void refreshScheduleJobs() {
		log.info("{},启动扫描定时新任务", System.currentTimeMillis());
		ScheduleJob schedule = new ScheduleJob();
		schedule.setStatus(DataStatusDef.ENABLE.name());
		scheduleJobMapper.select(schedule).parallelStream().forEach(job -> {
			try {
				ScheduleJobStatusEnum jobStatus = ScheduleJobStatusEnum.trance(job.getJobStatus());
				if (null == jobStatus && StringUtil.isBlank(job.getCron())
						&& CommonUtil.isAnyEmpty(job.getJobName(), job.getJobGroup())) {
					return;
				}

				TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
				CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

				if (null == trigger && jobStatus == ScheduleJobStatusEnum.ENABLE) {// 说明本条任务还没有添加到quartz中
					// 创建JobDetail（数据库中job_name存的任务全路径，这里就可以动态的把任务注入到JobDetail中）
					JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(job.getJobName()))
							.withIdentity(job.getJobName(), job.getJobGroup()).build();
					// 表达式调度构建器
					CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCron());
					// 按新的cronExpression表达式构建一个新的trigger
					trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())
							.withSchedule(scheduleBuilder).build();
					// 把trigger和jobDetail注入到调度器
					scheduler.scheduleJob(jobDetail, trigger);

					return;
				}

				if (jobStatus == ScheduleJobStatusEnum.DISABLE) {// 如果是禁用，从quartz中删除这条任务
					JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
					scheduler.pauseJob(jobKey);// 停止触发器
					scheduler.unscheduleJob(triggerKey);// 移除触发器
					scheduler.deleteJob(jobKey);// 删除任务
					return;
				}

				if (CommonUtil.isNotEmpty(trigger) && !trigger.getCronExpression().equals(job.getCron())) {
					// 说明该任务有变化，需要更新quartz中的对应的记录
					// 表达式调度构建器
					CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCron());
					// 按新的cronExpression表达式重新构建trigger
					trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder)
							.build();
					// 按新的trigger重新设置job执行
					scheduler.rescheduleJob(triggerKey, trigger);
				}
			} catch (Exception e) {
				log.error("扫描定时任务{},失败", job.toString(), e);
			}
		});
		log.info("{},定时任务扫描结束", System.currentTimeMillis());
	}

}
